﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;
using Cms.Utilities;
using Cms.Web.Framework.Extensions;
using Cms.Web.Framework.Infrastructure;
using iLib.Web.UI;

namespace Cms.Web.Framework.Controls
{
    public class Menu : Control, INamingContainer
    {
        private bool _clientScriptsAdded;
        private IList<int> _expandedIDs;
        private int _levels;
        private int _localeID;
        private Domain.Menu _menu;
        private int _menuID;
        private Domain.Page _page;

        public int MenuID
        {
            get { return _menuID; }
            set { _menuID = value; }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            _localeID = RequestDetails.Current.Locale.ID;
            _page = RequestDetails.Current.Page;
            ISiteRepository siteRepository = new SiteRepository();
            _menu = siteRepository.GetMenu(_menuID, RequestDetails.Current.Locale.Site.ID);
            if (_menu == null)
                return;
            var divContainer = new HtmlGenericControl("div");
            Controls.Add(divContainer);

            // Create container div
            divContainer.AddClass("cmsMenu" + _menuID);

            // Get expanded items from cookie
            _expandedIDs = new List<int>();
            HttpCookie cookie = HttpContext.Current.Request.Cookies["cmsMenu" + _menuID];
            if (cookie != null && !RequestDetails.Current.IsSystemPageRequest())
            {
                try
                {
                    string[] ids = HttpContext.Current.Server.UrlDecode(cookie.Value).Split(",".ToCharArray());
                    foreach (string id in ids.Where(id => !string.IsNullOrEmpty(id)))
                    {
                        _expandedIDs.Add(int.Parse(id));
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogError("Menu.CreateChildControls failed", ex);
                    cookie.Value = null;
                    cookie.Expires = DateTime.Now.AddYears(-1);
                    HttpContext.Current.Response.Cookies.Add(cookie);
                }
            }

            // Clean up expanded IDs
            if (_menu.SubMenuBehaviour == SubMenuBehaviour.OpenExclusively)
            {
                _expandedIDs = _menu.CleanExpandedIDs(_page, _expandedIDs);
            }

            // Render all levels recursively
            RenderItems(divContainer, _menu.PublishedVersion.GetRootItems());
            MarkParentItems(divContainer.Controls[0]);
            ExpandActiveItemsWithSubItems(divContainer.Controls[0]);
            _levels = 1;
            DetermineLevels(divContainer.Controls[0]);
            ((HtmlGenericControl)divContainer.Controls[0]).AddClass("level" + _levels);

            // Add the stylesheet to the head tag
            Page.Header.Controls.Add(
                new LiteralControl(@"<link type=""text/css"" rel=""stylesheet"" href=""__System/Menus/" +
                                   _menuID + @".css?v=" + _menu.StyleSheet.CurrentVersion.Timestamp + @""" />"));
        }

        private void RenderItems(HtmlControl container, IList<MenuItem> items)
        {
            // Create an UL for this level
            var ul = new HtmlGenericControl("ul");

            // If we are below the top level, check if the parent menu item id is in the expandedIDs array.
            if (_expandedIDs.Any(id => items[0].Parent != null && id == items[0].Parent.ID))
            {
                if (container.HasClass("active"))
                {
                    ul.AddClass("expanded");
                    ul.Style.Add("display", "block");
                }
            }
            container.Controls.Add(ul);
            foreach (MenuItem item in items)
            {
                // Administrators can always see all menu items, regardless of is they are published or not.
                // Regular users can only see menu items that are published and who's underlying page is published. 
                if (RequestDetails.Current.AdministratorID == -1 &&
                    (!item.IsPublishedNow() || (item.LinkType == MenuItemLinkType.Page && !item.Page.IsPublishedNow())))
                    continue;
                var li = new MenuItemListItem();
                if (item == items.First())
                    li.AddClass("first");
                if (item == items.Last() && item != items.First())
                    li.AddClass("last");
                ul.Controls.Add(li);
                var a = new HtmlAnchor();
                MenuItemTranslation translation = item.GetTranslation(_localeID);
                a.InnerHtml = HttpUtility.HtmlEncode(translation.Name);
                switch (item.LinkType)
                {
                    case MenuItemLinkType.Page:
                        a.HRef = "~/";
                        if (RequestDetails.Current.FirstFolderIsLocaleCode)
                            a.HRef += RequestDetails.Current.Locale.UrlCode + "/";
                        a.HRef += item.Page.GetVirtualPath();
                        if (_page != null &&
                            (_page.ID == item.Page.ID ||
                             (item.ActiveForPagesInCategory != null && item.ActiveForPagesInCategory.ContainsPage(_page))))
                        {
                            a.AddClass("active");
                            li.AddClass("active");
                            if (!string.IsNullOrEmpty(item.Version.Parent.ActiveItemPrefix))
                                a.InnerHtml = item.Version.Parent.ActiveItemPrefix + a.InnerHtml;
                            if (_menu.ItemIsActiveWhenSubItemIsActive)
                            {
                                Control parent = null;
                                if (li.Parent != null && li.Parent.Parent != null)
                                    parent = li.Parent.Parent;
                                while (parent != null && parent is HtmlContainerControl)
                                {
                                    if (((HtmlContainerControl)parent).TagName == "li")
                                    {
                                        ((HtmlAnchor)parent.Controls[0]).AddClass("active");
                                    }
                                    if (parent.Parent != null && parent.Parent.Parent != null)
                                        parent = parent.Parent.Parent;
                                }
                            }
                        }
                        break;
                    case MenuItemLinkType.Url:
                        a.HRef = item.GetTranslation(_localeID).Url;
                        break;
                    case MenuItemLinkType.Nothing:
                        a.HRef = "javascript:void(0);";
                        break;
                }
                if (item.Target != null)
                    a.Target = "_blank";

                if (_menu.ExtraWrapperSpans > 0)
                {
                    int spanCount = 0;
                    while (spanCount < _menu.ExtraWrapperSpans)
                    {
                        a.InnerHtml = "<span>" + a.InnerHtml + "</span>";
                        spanCount++;
                    }
                }
                li.Controls.Add(a);
                li.Controls.Add(a);
                li.ID = "mi" + _menuID + "-" + item.ID;
                if (item.SubItems.Count > 0)
                {
                    AddClientScripts();
                }
                if (item.SubItems.Count > 0)
                    RenderItems(li, item.SubItems);
            }
        }
        
        private void MarkParentItems(Control parentUl)
        {
            foreach (HtmlGenericControl li in parentUl.Controls)
            {
                if (li.Controls.Count == 2)
                {
                    li.AddClass("parent");
                    MarkParentItems(li.Controls[1]);
                }
            }
        }
        
        private void ExpandActiveItemsWithSubItems(Control parentUl)
        {
            foreach (HtmlGenericControl li in parentUl.Controls)
            {
                // Check if the menu item link is active and that the menu item has subitems
                if (((HtmlAnchor)li.Controls[0]).HasClass("active") && li.Controls.Count == 2)
                {
                    // Expand the subitems ul
                    ((HtmlGenericControl)li.Controls[1]).AddClass("expanded");
                    ((HtmlGenericControl)li.Controls[1]).Style.Add("display", "block");
                    ExpandActiveItemsWithSubItems(li.Controls[1]);
                }
            }
        }

        private void DetermineLevels(Control ul)
        {
            foreach (Control li in ul.Controls)
                if (li.Controls.Count == 2 && ((HtmlGenericControl)li.Controls[1]).HasClass("expanded"))
                {
                    // Contains another ul
                    int level = 2;
                    Control parent = li.Parent;
                    while (parent is HtmlContainerControl && ((HtmlContainerControl)parent).TagName != "div")
                    {
                        if (((HtmlContainerControl)parent).TagName != "ul")
                            level++;
                        parent = parent.Parent;
                    }
                    if (level > _levels)
                        _levels = level;
                    DetermineLevels(li.Controls[1]);
                }
        }
        
        private void AddClientScripts()
        {
            if (_clientScriptsAdded || RequestDetails.Current.IsSystemPageRequest())
                return;
            Page.AddHeaderScriptInclude(ConfigurationManager.AppSettings["jQueryPath"]);
            Page.AddHeaderScriptInclude(iLibRegistrator.GetScriptUrl(Page));
            Page.AddHeaderScriptInclude("Admin/Js/CmsLib.js");
            Page.AddHeaderScriptInclude("Admin/Js/Menu.js?v=23");
            Page.ClientScript.RegisterStartupScript(GetType(), string.Empty, "cms.Menu.Init(" + _menuID + ", \"" + _menu.SubMenuBehaviour + "\");", true);
            _clientScriptsAdded = true;
        }
    }
}